/*
 *
 * Copyright (C) 2015 Toggl Desktop developers.
 * Copyright (C) 2023 KylinSoft Co., Ltd.
 *
 *
 * You may use this file under the terms of the BSD license as follows:
 *
 * "Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
 *     of its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 */

#include "singleApplication.h"
#include <QtNetwork/QLocalSocket>
#include <QFileInfo>

#define TIME_OUT                (500)    // 500ms

SingleApplication::SingleApplication(int &argc, char **argv)
    : QApplication(argc, argv)
    , w(NULL)
    , _isRunning(false)
    , _localServer(NULL) {

    // 取应用程序名作为LocalServer的名字
    // Take the application name as the name of localserver
    _serverName = QFileInfo(QCoreApplication::applicationFilePath()).fileName() + QLatin1String(getenv("DISPLAY"));
    _initLocalConnection();
}


// 检查是否已經有一个实例在运行, true - 有实例运行， false - 没有实例运行
// Check whether there is an instance running, true - there is an instance running, false - there is no instance running
bool SingleApplication::isRunning() {
    return _isRunning;
}


// 通过socket通讯实现程序单实例运行，监听到新的连接时触发该函数
// The single instance of the program is run through socket communication, which is triggered when listening to a new connection
void SingleApplication::_newLocalConnection()
{
    //返回下一个挂起的连接，作为连接的QLocalSocket对象。
    QLocalSocket *socket = _localServer->nextPendingConnection();
    if (socket) {
        //该函数将阻塞，直到可以读取数据并且发出readyRead（）信号为止。 该函数将在毫秒毫秒后超时； 默认超时为30000毫秒。
       // 如果有可供读取的数据，则该函数返回true；否则返回false 否则返回false（如果发生错误或操作超时）。
        socket->waitForReadyRead(2*TIME_OUT);
        //关闭socket
        delete socket;

        // 其他处理，如：读取启动参数
        // Other processing, such as reading start parameters
        _activateWindow();
    }
}

// 通过socket通讯实现程序单实例运行，
// Through socket communication, the single instance of program can be run,
// 初始化本地连接，如果连接不上server，则创建，否则退出
// Initialize the local connection. If the server cannot be connected, create it. Otherwise, exit
void SingleApplication::_initLocalConnection()
{
    _isRunning = false;

    QLocalSocket socket;
    socket.connectToServer(_serverName);
    //建立链接，超时时间500ms
    if (socket.waitForConnected(TIME_OUT)) {
        fprintf(stderr, "%s already running.\n",
                _serverName.toLocal8Bit().constData());
        //有连接，改状态，直接返回
        _isRunning = true;
        // 其他处理，如：将启动参数发送到服务端
        // Other processing, such as sending startup parameters to the server
        return;
    }

    //连接不上服务器，就创建一个
    // If you can't connect to the server, create a
    _newLocalServer();
}

// 创建LocalServer
// Create localserver
void SingleApplication::_newLocalServer()
{
    _localServer = new QLocalServer(this);
    //信号：每当有新的连接
    connect(_localServer, SIGNAL(newConnection()), this, SLOT(_newLocalConnection()));
    if (!_localServer->listen(_serverName)) {
        // 此时监听失败，可能是程序崩溃时,残留进程服务导致的,移除之
        // At this time, listening fails. It may be caused by the residual process service when the program crashes. Remove it
        if (_localServer->serverError() == QAbstractSocket::AddressInUseError) {
            QLocalServer::removeServer(_serverName); // <-- 重点  a key
            _localServer->listen(_serverName); // 再次监听
                                               // Monitor again
        }
    }
}

// 激活主窗口
// Activate main window
void SingleApplication::_activateWindow() {
    if (w) {
        w->show();
        w->raise();
        w->activateWindow(); // 激活窗口
                             //  Activate window
    }
}
